function opts = slsetopts(opts, varargin)
%SLSETOPTS Sets/Updates the options with name/value list or another struct
%
% [ Syntax ]
%   - opts = slsetopts([], name1, value1, name2, value2, ...)
%   - opts = slsetopts(opts, name1, value1, name2, value2, ...)
%   - opts = slsetopts(opts, srcopts)
%
% [ Arguments ]
%   - opts = slsetopts([], name1, value1, name2, value2, ...) makes an
%     struct of options. 
%
%     The struct opts will consist of fields named with those given in
%     name1, name2, ..., and whose values are given in value1, value2, ...
%     correspondingly.
%
%     Different from the standard struct function, a scalar struct is 
%     always returned. When value is input as a cell array, it is
%     considered as the value of the corresponding value as a whole.
%
%   - opts = slsetopts(opts, name1, value1, name2, value2, ...) updates
%     an existing option struct with name value list. 
%
%     Note that only the names that are field names of input opts are 
%     allowed in the name/value list. If there are multiple pairs with
%     the same name in the list, the rightmost one takes effect.
%
%   - opts = slsetopts(opts, srcopts) updates an existing option struct
%     based on the fields in a source struct.
%
% [ History ]
%   - Created by Dahua Lin, on Dec 25, 2007
%

%% main

if nargin < 2
    return;
end

if isempty(opts)                % create new option struct    
    [names, values] = parse_name_value_list(varargin);        
    opts = create_options(names, values);
    
elseif isstruct(opts) && numel(opts) == 1    % update existing struct
    
    if ischar(varargin{1}) && length(varargin) >= 2
        
        [names, values] = parse_name_value_list(varargin);
        opts = update_options(opts, names, values);        
        
    elseif isstruct(varargin{1}) && length(varargin) == 1
        
        [names, values] = parse_src_struct(varargin{1});
        opts = update_options(opts, names, values);
        
    else
        error('sltoolbox:slsetopts:invalidarg', ...
            'Invalid input arguments to slsetopts');
    end
    
else
    error('sltoolbox:slsetopts:invalidarg', ...
        'opts should be either empty or a struct scalar');
end


%% supporting functions

function [names, values] = parse_name_value_list(nvlist)

names = nvlist(1:2:end);
values = nvlist(2:2:end);

if length(names) ~= length(values)
    assert('sltoolbox:slsetopts:invalidarg', ...
        'The names and values should form pairs.');
end

assert(iscellstr(names), 'sltoolbox:slsetopts:invalidarg', ...
    'The name value list is invalid.');


function [names, values] = parse_src_struct(S)

names = fieldnames(S);
values = cellfun(@(x) S.(x), names, 'UniformOutput', false);


function opts = create_options(names, values)

cv = cellfun(@(x) {x}, values, 'UniformOutput', false);
nvs = [names; cv];

opts = struct(nvs{:});


function opts = update_options(opts, names, values)

for i = 1 : numel(names)
    cn = names{i};
    
    if isfield(opts, cn)
        opts.(cn) = values{i};
    else
        error('sltoolbox:slsetopts:unknownname', ...
            'The option name %s is unknown', cn);
    end
end




